﻿#region Copyright and License Notice
// Copyright (C)2010-2016 - Rob Levine and other contributors
// http://configgen.codeplex.com
// 
// This file is part of ConfigGen.
// 
// ConfigGen is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// ConfigGen is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License and 
// the GNU Lesser General Public License along with ConfigGen.  
// If not, see <http://www.gnu.org/licenses/>
#endregion

using System;
using System.Diagnostics;
using System.Linq;
using System.Text;
using ConfigGen.ConsoleApp.Core;
using ConfigGen.ConsoleApp.Core.CommandLineOptionCore;
using ConfigGen.ConsoleApp.Core.CommandLineOptions;
using ConfigGen.Core;
using ConfigGen.Utilities.Extensions.System;
using log4net;
using log4net.Appender;
using log4net.Config;
using log4net.Core;
using log4net.Filter;
using log4net.Layout;
using Microsoft.Practices.Unity;

namespace ConfigGen.ConsoleApp
{
    class Program
    {
        private static readonly ILog Log = LogManager.GetLogger(typeof (Program));

        private static readonly ConfigGenCommandLineOptionFactory CommandLineArgFactory = new ConfigGenCommandLineOptionFactory();
        private static readonly ColoredConsoleAppender ConsoleOutAppender = new ColoredConsoleAppender();
        private static readonly ColoredConsoleAppender ConsoleErrorAppender = new ColoredConsoleAppender();
        private static bool _verboseLogging = true;
        private static bool _warningsGenerated;

        private static void Main(string[] args)
        {
            ConfigureLogging(false);

            ShowTitle();

            try
            {
                if (args.Contains("--help", StringComparer.OrdinalIgnoreCase)
                    || args.Contains("-help", StringComparer.OrdinalIgnoreCase)
                    || args.Contains("-?", StringComparer.OrdinalIgnoreCase)
                    || args.Contains("--?", StringComparer.OrdinalIgnoreCase))
                {
                    ShowHelp();
                    return;
                }

                var consoleArgumentParser = new CommandLineOptionExecutor<ConsoleAppPreferences>(CommandLineArgFactory);

                var settings = new ConsoleAppPreferences
                    {
                        OutputDirectory = "Configs",
                        SettingsFile = "App.Config.Settings.xls",
                        TemplateFile = "App.Config.Template.xml"
                    };
                try
                {
                    consoleArgumentParser.ProcessAndExecuteCommandLineOptions(args, settings);
                }
                catch (Exception ex)
                {
                    Log.Info("Error parsing command line: " + ex.Message);
                    Environment.ExitCode = 1;
                    ShowHelp();
                    return;
                }

                _verboseLogging = settings.Verbose;

                if (settings.Verbose)
                {
                    ConfigureLogging(true);
                }

                var container = new ConfigGenContainer();
                var generator = container.Resolve<ConfigurationFilesGenerator>();

                var numFilesGenerated = generator.GenerateFiles(settings);
                if (numFilesGenerated == 0)
                {
                    Log.Info("No files generated");
                    return;
                }


                foreach (var result in generator.Results)
                {
                    var fileIsTheSame = result.OldFileMd5Hash.IsEqualTo(result.NewFileMd5Hash);
                    if (settings.ErrorOnFileChanged && !fileIsTheSame)
                    {
                        Environment.ExitCode = 2;
                    }
                    var fileOutput = string.Format("{0} - {1}",
                                                   result.MachineName.PadRight(20, ' '),
                                                   fileIsTheSame
                                                       ? "[FILE UNCHANGED]"
                                                       : "[FILE CHANGED]");

                    var hasWarnings = result.HasWarnings;
                    if (settings.ErrorOnFileChanged && !fileIsTheSame)
                    {
                        Log.ErrorFormat("{0} - IN ERROR (\"error-on-file-changed\" option in command)", fileOutput);
                        Environment.ExitCode = 2;
                    }
                    else
                    {
                        _warningsGenerated |= hasWarnings;
                        if (hasWarnings)
                        {
                            Log.WarnFormat("{0} - WITH WARNINGS", fileOutput);
                        }
                        else
                        {
                            Log.InfoFormat("{0} - WITHOUT WARNINGS", fileOutput);
                        }
                    }
                    if (hasWarnings)
                    {
                        foreach (var item in result.UnrecognisedTokenCounts.Where(t => t.Value > 0))
                        {
                            Log.Warn("Unrecognised token in template: " + item.Key);
                        }

                        foreach (var item in result.TokenUseCounts.Where(t => t.Value == 0))
                        {
                            Log.Warn("Unused token in settings: " + item.Key);
                        }
                        Log.Info(String.Empty);
                    }
                }


                if (_warningsGenerated)
                {
                    if (settings.ErrorOnWarnings)
                    {
                        Environment.ExitCode = 1;
                    }
                    Log.Warn(String.Empty);
                    Log.Warn("Configuration generation completed with warnings");
                    Log.Warn(String.Empty);
                }
                else
                {
                    Log.Info(String.Empty);
                    Log.Info("Configuration generation completed without warnings");
                    Log.Info(String.Empty);
                }
            }
            catch (Exception ex)
            {
                if (_verboseLogging)
                {
                    Log.Error("Unhandled exception:");
                    Log.Error(ex);
                }
                else
                {
                    Log.Error(ex.Message);
                }
                Environment.ExitCode = 2;
            }
            finally
            {
                Log.DebugFormat("Program exited with code {0}", Environment.ExitCode);
                if (Debugger.IsAttached)
                {
                    Log.Info("Debugger attached - press enter to exit");
                    Console.ReadLine();
                }
            }
        }

        private static void ShowTitle()
        {
            var version = typeof(ConfigurationFilesGenerator).Assembly.GetName().Version;
            Log.InfoFormat("ConfigGen v{0} - Configuration file generation tool", version);
            Log.Info("Copyright (C)2010-2013 - Rob Levine and other contributors - http://configgen.codeplex.com");
            Log.Info("--");
        }

        private static void ConfigureLogging(bool verbose)
        {
            const string layout = @"%message%newline";
            ConsoleOutAppender.Threshold = verbose ? Level.Debug : Level.Info;
            ConsoleOutAppender.AddFilter(new LevelMatchFilter() { AcceptOnMatch = false, LevelToMatch = Level.Error});
            ConsoleOutAppender.AddFilter(new LevelMatchFilter() { AcceptOnMatch = false, LevelToMatch = Level.Fatal });
            ConsoleOutAppender.Layout = new PatternLayout(layout);
            ConsoleOutAppender.AddMapping(new ColoredConsoleAppender.LevelColors { Level = Level.Warn, ForeColor = ColoredConsoleAppender.Colors.Yellow | ColoredConsoleAppender.Colors.HighIntensity });
            ConsoleOutAppender.AddMapping(new ColoredConsoleAppender.LevelColors { Level = Level.Info, ForeColor = ColoredConsoleAppender.Colors.White | ColoredConsoleAppender.Colors.HighIntensity });
            ConsoleOutAppender.ActivateOptions();
            BasicConfigurator.Configure(ConsoleOutAppender);

            ConsoleErrorAppender.Target = "Console.Error";
            ConsoleErrorAppender.Threshold = Level.Error;
            ConsoleErrorAppender.Layout = new PatternLayout(layout);
            ConsoleErrorAppender.AddMapping(new ColoredConsoleAppender.LevelColors
                                                {
                                                    Level = Level.Error,
                                                    ForeColor =
                                                        ColoredConsoleAppender.Colors.Red |
                                                        ColoredConsoleAppender.Colors.HighIntensity
                                                });
            ConsoleErrorAppender.ActivateOptions();
            BasicConfigurator.Configure(ConsoleErrorAppender);
        }

        private static void ShowHelp()
        {
            Log.Info(String.Empty);
            Log.Info("USAGE: cfg.exe [options]");
            Log.Info(String.Empty);
            Log.Info("WHERE OPTIONS: ");
            Log.Info(String.Empty);
            foreach (var command in CommandLineArgFactory.LoadedHandlers)
            {
                Log.Info(GetHelpTextForCommand(command));
            }
            Log.Info(String.Empty);
            Log.Info("cfg.exe with no options is equivalent to:");
            Log.Info(String.Empty);
            Log.Info("cfg.exe -s App.Config.Settings.xls -t App.Config.Template.xml -o Configs -ves \"EmptyString\"");
        }

        private static string GetHelpTextForCommand(ICommandLineOption<ConsoleAppPreferences> command)
        {
            var sb = new StringBuilder();
            sb.AppendFormat("-{0} {1}\n", command.ShortName, command.ArgsUsage);
            sb.AppendFormat(" or --{0} {1}\n", command.LongName, command.ArgsUsage);
            foreach (var line in command.HelpText.WordWrap(80))
            {
                sb.AppendFormat("    {0}\n", line);
            }
            return sb.ToString();
        }
    }
}
